﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Remoting.Lifetime;
using System.Diagnostics;
using System.Runtime.Remoting;
using System.Collections;

namespace J9Script.Internal
{
    // Based on .net ClientSponsor class and code from: http://msdn.microsoft.com/en-us/magazine/cc300474.aspx
    // I changed part of this to be more like ClientSponsor.

    // This is almost the same thing as System.Runtime.Remoting.Lifetime.ClientSponsor except it returns lease.InitialLeaseTime instead of a configured amount of time.
    internal class SponsorshipManager : MarshalByRefObject, ISponsor
    {
        Hashtable _leases;
        private readonly object _leases_lock = new object();

        public SponsorshipManager()
        {
            _leases = new Hashtable(10);
        }

        TimeSpan ISponsor.Renewal(ILease lease)
        {
            return lease.InitialLeaseTime;
        }

        public bool Register(IScriptObject obj)
        {
            MarshalByRefObject o = obj as MarshalByRefObject;
            if (obj != null)
                return Register(o);
            else
                return false;
        }

        /// <summary>
        /// Register accepts a remote object, extracts the lease from it, registers SponsorshipManager as the sponsor, and adds the lease to the internal list. 
        /// </summary>
        /// <param name="obj"></param>
        public bool Register(MarshalByRefObject obj)
        {
            ILease lease = (ILease)RemotingServices.GetLifetimeService(obj);

            if (lease == null) // null means infinate lifetime which can't be registered.
                return false;

            lease.Register(this);
            lock (_leases_lock)
            {
                _leases[obj] = lease;
            }

            return true;
        }

        /// <summary>
        /// Removes the lease associated with the specified object from the list and unregisters SponsorshipManager as a sponsor.
        /// </summary>
        /// <param name="obj"></param>
        public void Unregister(MarshalByRefObject obj)
        {
            ILease lease = null;
            lock (_leases_lock)
            {
                if (_leases.ContainsKey(obj) == false)
                    return;

                lease = (ILease)_leases[obj];
                _leases.Remove(obj);
            }

            if (lease != null)
            {
                lease.Unregister(this);
            }
        }

        public void UnregisterAll()
        {
            try
            {
                lock (_leases_lock)
                {
                    IDictionaryEnumerator enumerator = _leases.GetEnumerator();
                    while (enumerator.MoveNext())
                    {
                        try
                        {
                            ((ILease)enumerator.Value).Unregister(this); // this can throw RemotingException: Requested Service not found.  I saw this and think it happened after the object no longer had any references and was GC'd much later.
                        }
                        catch
                        {
                            // do nothing
                        }
                    }
                    this._leases.Clear();
                }
            }
            catch
            {
                // do nothing
            }
        }

        ~SponsorshipManager()
        {
            try
            {
                UnregisterAll();
            }
            catch
            {
                // do nothing.
            }
        }

    }
}
